package com.ecreditpal.audio.wav;


import com.ecreditpal.audio.jave.Encoder;
import com.ecreditpal.audio.jave.MultimediaInfo;
import com.ecreditpal.audio.util.BaseUtil;
import lombok.extern.slf4j.Slf4j;

import javax.sound.sampled.*;
import java.io.*;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lifeng
 * @version 1.0 on 2018/6/15.
 */
@Slf4j
public class Wav {
    private static final String TARGET_PATH = "/Users/lifeng/Desktop/partition";

    public static List<byte[]> split(InputStream is, int interval) throws IOException {
//        int index = path.lastIndexOf(".");
//        String prefix = path.substring(0,index);
//        String postfix = path.substring(index);
//        String fileName = path.substring(path.lastIndexOf("/") + 1, index);

        BufferedInputStream bis = new BufferedInputStream(is);
        File file = File.createTempFile("temp", ".wav");
        FileOutputStream fis = new FileOutputStream(file);

        bis.mark(Integer.MAX_VALUE);
        int b;
        while ((b = bis.read()) != -1) {
            fis.write(b);
        }
        fis.flush();
        fis.close();

        bis.reset();
        long timeLen = getTimeLen(file);

        long part = timeLen % interval == 0 ? timeLen / interval : timeLen / interval + 1;
        List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < part; i++) {
            log.info("start split for file,start:{},end:{}", i * interval, (i + 1) * interval);
            byte[] a = cut(bis, i * interval, (i + 1) * interval, timeLen, file.length());
            list.add(a);
        }
        bis.close();
        file.deleteOnExit();
        return list;
    }

    public static void main(String[] args) throws LineUnavailableException,
            UnsupportedAudioFileException, IOException {
//        cutSingle();

        InputStream is = new FileInputStream("/Users/lifeng/Desktop/1.wav");
        byte[] b = new byte[is.available()];
        is.read(b);
        long time = (long) getWavLength(b);
//        split(new FileInputStream("/Users/lifeng/Desktop/pp.wav"), 10);
    }


    /**
     * 通过工具获取时长
     *
     * @param file
     * @return
     */
    public static long getTimeLen(File file) {
        long tlen = 0;
        if (file != null && file.exists()) {
            Encoder encoder = new Encoder(new MyFFMPEGExecute());
            try {
                MultimediaInfo m = encoder.getInfo(file);
                long ls = m.getDuration();
                tlen = ls / 1000;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return tlen;

    }


    /**
     * 获取wav音频时长 ms
     *
     * @param wavdata wav音频文件字节数组
     * @return
     */
    public static double getWavLength(byte[] wavdata) {
        if (wavdata != null && wavdata.length > 44) {
            int byteRate = BaseUtil.bytes2Int(wavdata, 28, 31);
            int waveSize = BaseUtil.bytes2Int(wavdata, 40, 43);
            return new BigDecimal(waveSize).divide(new BigDecimal(byteRate)).doubleValue();
        }
        return 0;
    }

    public static double getWavLengthNoBody(byte[] wavdata, int byteRate) {
        return new BigDecimal(wavdata.length).divide(new BigDecimal(byteRate)).doubleValue();
    }


    public static void cutSingle() throws IOException {
        File file = new File("/Users/lifeng/Desktop/pp.wav");
        cut(new BufferedInputStream(new FileInputStream(file)), 10, 20, getTimeLen(file), file.length());
    }


    /**
     * 通过时间截取wav文件数据体
     *
     * @param source     源文件
     * @param start      开始时间
     * @param end        结束时间
     * @param simpleRate 采样率
     * @return
     */
    public static byte[] cutBody(byte[] source, double start, double end, int simpleRate) {

        int splitSize = (int) ((end - start) * simpleRate);
        int skipSize = (int) ((simpleRate * start));
        byte[] splited = new byte[splitSize];

        System.arraycopy(source, skipSize, splited, 0, splited.length);

        return splited;
    }


    public static byte[] cutSingle(byte[] source, double start, int simpleRate) {

        int skipSize = (int) (start * simpleRate);
        byte[] splited = new byte[source.length - skipSize];

        System.arraycopy(source, skipSize, splited, 0, splited.length);

        return splited;
    }

    public static byte[] cut(InputStream sourcefile, int start, int end, long total, long fileSize) throws IOException {
        end = end > total ? (int) total : end;
        // long t1 = getTimeLen(wav);  //总时长(秒)
        if (start < 0 || end <= 0 || start >= total || start >= end) {
            return null;
        }
//        sourcefile.reset();
//        FileInputStream fis = new FileInputStream(wav);

        long wavSize = fileSize - 44;  //音频数据大小（44为wav文件头长度）
        long splitSize = (wavSize / total) * (end - start);  //截取的音频数据大小
        long skipSize = (wavSize / total) * start;  //截取时跳过的音频数据大小
        int splitSizeInt = Integer.parseInt(String.valueOf(splitSize));
        int skipSizeInt = Integer.parseInt(String.valueOf(skipSize));

        ByteBuffer buf1 = ByteBuffer.allocate(4);  //存放文件大小,4代表一个int占用字节数

        buf1.putInt(splitSizeInt + 36);  //放入文件长度信息
        byte[] flen = buf1.array();

        ByteBuffer buf2 = ByteBuffer.allocate(4);
        buf2.putInt(splitSizeInt);
        byte[] dlen = buf2.array();

        flen = reverse(flen);  //数组反转
        dlen = reverse(dlen);


        byte[] head = new byte[44];  //定义wav头部信息数组
        sourcefile.read(head, 0, head.length);  //读取源wav文件头部信息
        for (int i = 0; i < 4; i++) {  //4代表一个int占用字节数
            head[i + 4] = flen[i];  //替换原头部信息里的文件长度
            head[i + 40] = dlen[i];  //替换原头部信息里的数据长度
        }

        byte[] fbyte = new byte[splitSizeInt + head.length];  //存放截取的音频数据
        for (int i = 0; i < head.length; i++) {  //放入修改后的头部信息
            fbyte[i] = head[i];
        }

        byte[] skipBytes = new byte[skipSizeInt];  //存放截取时跳过的音频数据
        sourcefile.read(skipBytes, 0, skipBytes.length);  //跳过不需要截取的数据
        sourcefile.read(fbyte, head.length, fbyte.length - head.length);  //读取要截取的数据到目标数组
        sourcefile.reset();
        sourcefile.close();
        return fbyte;
    }


    public static byte[] reverse(byte[] array) {
        byte temp;
        int len = array.length;
        for (int i = 0; i < len / 2; i++) {
            temp = array[i];
            array[i] = array[len - 1 - i];
            array[len - 1 - i] = temp;
        }
        return array;
    }

}
